/*
 * Copyright (C) 2024 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "napi/native_api.h"
#include "main.h"
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <iostream>

#define LOG_DOMIN 0x3200
#define LOG_TAG "MY_NDKDEMO"


#include <multimedia/player_framework/avplayer.h>
#include <multimedia/player_framework/avplayer_base.h>
#include <multimedia/player_framework/avplayer_base.h>
#include <multimedia/player_framework/native_averrors.h>
#include "native_image/native_image.h"
#include "native_window/external_window.h"
#include "camera_manager.h"
#include "GLES3/gl32.h"
#include <typeinfo>
#include <fcntl.h>
#include <string>
#include <arpa/nameser.h>
#include <fcntl.h>
#include <js_native_api.h>
#include <sys/stat.h>
#include <unistd.h>
#include <vector>
#include <thread>

using namespace std;
using namespace OHOS_CAMERA_SAMPLE;
static NDKCamera *ndkCamera_ = nullptr;

static OH_AVRecorder_Config *config;
char *previceId_ = nullptr;
char *videoSrufaceId;
// 设置状态回调
void OnStateChange(OH_AVRecorder *recorder, OH_AVRecorder_State state, OH_AVRecorder_StateChangeReason reason,
                   void *userData) {
    (void)recorder;
    (void)userData;
    const char *reasonStr = (reason == USER) ? "USER" : (reason == BACKGROUND) ? "BACKGROUND" : "UNKNOWN";
}
void OnError(OH_AVRecorder *recorder, int32_t errorCode, const char *errorMsg, void *userData) {
    (void)recorder;
    (void)userData;
}

static napi_value createAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = AV_ERR_OK;
    g_avRecorder = OH_AVRecorder_Create();
    if (g_avRecorder == nullptr) {
        result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}
static napi_value setPreviewSurfaceId(napi_env env, napi_callback_info info) {

    (void)info;
    // todo
    int result = AV_ERR_OK;
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}


static bool GetInt32Property(napi_env env, napi_value root, const char *utf8name, int32_t *res) {
    napi_value property = nullptr;
    auto status = napi_get_named_property(env, root, utf8name, &property);
    if (status != napi_ok || property == nullptr) {
//         OH_LOG_ERROR("Get property error %{public}s", utf8name);
        return false;
    }
    return (napi_get_value_int32(env, property, res) == napi_ok);
}


static bool parseSetConfigOps(napi_env env, napi_value arg, struct OH_AVRecorder_Config &config) {

    if (env == nullptr || arg == nullptr) {
//         DEBUG_LOG("env is %{public}s || arg is %{public}s", DEBUG_PTR(env), DEBUG_PTR(arg));
        return false;
    }
    // Optional parameters, no need check error.
    GetInt32Property(env, arg, "audioBitrate", &(config.profile.audioBitrate));
    GetInt32Property(env, arg, "audioChannels", &(config.profile.audioChannels));
    GetInt32Property(env, arg, "audioSampleRate", &(config.profile.audioSampleRate));

    GetInt32Property(env, arg, "videoBitrate", &(config.profile.videoBitrate));
    GetInt32Property(env, arg, "videoFrameWidth", &(config.profile.videoFrameWidth));
    GetInt32Property(env, arg, "videoFrameHeight", &(config.profile.videoFrameHeight));
    GetInt32Property(env, arg, "videoFrameRate", &(config.profile.videoFrameRate));
    int32_t fileFormat = -1;
    GetInt32Property(env, arg, "fileFormat", &fileFormat);
    switch (fileFormat) {
    case CFT_MPEG_4:
        config.profile.fileFormat = CFT_MPEG_4;
        break;
    case CFT_MPEG_4A:
        config.profile.fileFormat = CFT_MPEG_4A;
        break;
    case CFT_MP3:
        config.profile.fileFormat = CFT_MP3;
        break;
    case CFT_WAV:
        config.profile.fileFormat = CFT_WAV;
        break;
    default:
        config.profile.fileFormat = CFT_MPEG_4;
        break;
    }
    return true;
}

// 配置参数
void setConfig(OH_AVRecorder_Config &config) {
    config.audioSourceType = DEFAULT;
    config.videoSourceType = SURFACE_YUV;
    // 设置媒体属性
    config.profile.audioBitrate = 48000;
    config.profile.audioChannels = 2;
    config.profile.audioCodec = AUDIO_AAC;
    config.profile.audioSampleRate = 48000;

    config.profile.videoBitrate = 2000000;
    config.profile.videoFrameWidth = 1920;
    config.profile.videoFrameHeight = 1080;
    config.profile.videoFrameRate = 30;
    config.profile.videoCodec = VIDEO_AVC;

    config.profile.fileFormat = CFT_MPEG_4;
    config.fileGenerationMode = APP_CREATE;

    config.metadata.videoOrientation = (char *)malloc(2);
    if (config.metadata.videoOrientation != nullptr) {
        strcpy(config.metadata.videoOrientation, "0");
    }
    config.metadata.location.latitude = 0;
    config.metadata.location.longitude = 0;
}    
    
static napi_value prepareAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    OH_LOG_INFO(LOG_APP, "---------NDK xts------prepare AVRecorder in");
    struct OH_AVRecorder_Config config;
    setConfig(config);

    size_t argc = 2;
    napi_value args[2] = {nullptr};
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);

    char *fd = nullptr;
    size_t typeLen = 0;
    napi_get_value_string_utf8(env, args[1], nullptr, 0, &typeLen);
    fd = new char[typeLen + 1];
    napi_get_value_string_utf8(env, args[1], fd, typeLen + 1, &typeLen);
    config.url = fd;

    parseSetConfigOps(env,args[0],config);

    OH_LOG_INFO(LOG_APP, "---------AVRecorder config.url = fd: %{public}s", config.url);
    OH_LOG_INFO(LOG_APP, "---------AVRecorder config.profile.videoFrameWidth = fd: %{public}", 
        config.profile.videoFrameWidth);
    OH_LOG_INFO(LOG_APP, "---------AVRecorder config.url.profile.videoFrameHeight = fd: %{public}", 
        config.profile.videoFrameHeight);
    OH_AVRecorder_SetStateCallback(g_avRecorder, OnStateChange, nullptr);
    OH_AVRecorder_SetErrorCallback(g_avRecorder, OnError, nullptr);

    int result = AV_ERR_OK;
    result = OH_AVRecorder_Prepare(g_avRecorder, &config);
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    OH_LOG_INFO(LOG_APP, "---------NDK xts------prepare AVRecorder out");
    return res;
}

static napi_value createPrepareAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = AV_ERR_OK;
    g_avRecorder = OH_AVRecorder_Create();
    if (g_avRecorder == nullptr) {
        result = AV_ERR_UNKNOWN;
    }
    struct OH_AVRecorder_Config config1;
    config1.audioSourceType = DEFAULT;
    config1.videoSourceType = SURFACE_YUV;
    // 设置媒体属性
    config1.profile.audioBitrate = 48000;
    config1.profile.audioChannels = 2;
    config1.profile.audioCodec = AUDIO_AAC;
    config1.profile.audioSampleRate = 48000;

    config1.profile.videoBitrate = 2000000;
    config1.profile.videoFrameWidth = 1920;
    config1.profile.videoFrameHeight = 1080;
    config1.profile.videoFrameRate = 30;
    config1.profile.videoCodec = VIDEO_AVC;

    config1.profile.fileFormat = CFT_MPEG_4;
    config1.fileGenerationMode = APP_CREATE;

    config1.metadata.videoOrientation = (char *)malloc(2);
    if (config1.metadata.videoOrientation != nullptr) {
        strcpy(config1.metadata.videoOrientation, "0");
    }
    config1.metadata.location.latitude = 0;
    config1.metadata.location.longitude = 0;

    // 1.1 设置url
    const std::string AVRECORDER_ROOT = "/data/storage/el2/base/files/";
    int32_t outputFd = open((AVRECORDER_ROOT + "avrecorder001.mp4").c_str(), O_RDWR | O_CREAT, 0777);
    std::string fileUrl = "fd://" + std::to_string(outputFd);
    config1.url = const_cast<char *>(fileUrl.c_str());
    std::cout << "config1.url is:" << config->url << std::endl;

    OH_AVRecorder_SetStateCallback(g_avRecorder, OnStateChange, nullptr);
    OH_AVRecorder_SetErrorCallback(g_avRecorder, OnError, nullptr);

    int result1 = OH_AVRecorder_Prepare(g_avRecorder, &config1);
    if (result1 != AV_ERR_OK || result != AV_ERR_OK) {
        result = AV_ERR_UNKNOWN;
    }
    // todo

    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value startAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = OH_AVRecorder_Start(g_avRecorder);
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value pauseAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = OH_AVRecorder_Pause(g_avRecorder);
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value resumeAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = OH_AVRecorder_Resume(g_avRecorder);
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value stopAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = OH_AVRecorder_Stop(g_avRecorder);
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value resetAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = OH_AVRecorder_Reset(g_avRecorder);
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value releaseAVRecorder(napi_env env, napi_callback_info info) {
    (void)info;
    int result = OH_AVRecorder_Release(g_avRecorder);
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value getAVRecorderConfig(napi_env env, napi_callback_info info) {
    (void) info;
    
    OH_AVRecorder_Config *config;
    setConfig(*config);
    int result = OH_AVRecorder_GetAVRecorderConfig(g_avRecorder, &config);
    if (result != AV_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "---------NDK xts------getAVRecorderConfig error");
    }
    OH_LOG_INFO(LOG_APP, "---------NDK xts------getAVRecorderConfig sucuess", config -> profile.audioBitrate);
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value getAvailableEncoder(napi_env env, napi_callback_info info) {
    (void) info;
    
    OH_AVRecorder_EncoderInfo *encoderInfo = nullptr;
    
    int32_t lengthValue = 0;
    int32_t *length = &lengthValue;
    
    int result = OH_AVRecorder_GetAvailableEncoder(g_avRecorder, &encoderInfo, length);
    
    if (result != AV_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "---------NDK xts------getAvailableEncorder error");
    } else {
        if (encoderInfo != nullptr) {
             OH_LOG_INFO(LOG_APP, "Encorder Info in");
             OH_LOG_INFO(LOG_APP, "Encorder mimeType %{public}d", encoderInfo -> mimeType);
            
             OH_LOG_INFO(LOG_APP, "Encorder mimeType %{public}d", encoderInfo -> type);
            
            if (encoderInfo -> sampleRate != nullptr) {
                for (int i = 0; i < encoderInfo -> sampleRateLen; i++) {
                    OH_LOG_INFO(LOG_APP, "Encorder mimeType %{public}d", i, encoderInfo -> sampleRate[i]);
                }
            }
        } else {
             OH_LOG_INFO(LOG_APP, "EncorderInfo is null");
        }
    }

    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value updateRotationAVRecorder(napi_env env, napi_callback_info info) {

    (void)info;
    // todo
    int result = AV_ERR_OK;
    if (result != AV_ERR_OK) {
        //         result = AV_ERR_UNKNOWN;
    }
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}

static napi_value prepareCamera(napi_env env, napi_callback_info info) {
    size_t argc = 6;
    napi_value args[6] = {nullptr};
    size_t typeLen = 0;
    napi_get_cb_info(env, info, &argc, args, nullptr, nullptr);
    
    int32_t focusMode;
    napi_get_value_int32(env, args[0], &focusMode);
    
    uint32_t cameraDeviceIndex;
    napi_get_value_uint32(env, args[1], &cameraDeviceIndex);
    
    uint32_t sceneMode;
    napi_get_value_uint32(env, args[2], &sceneMode);

    char *previewId = nullptr;
    napi_get_value_string_utf8(env, args[3], nullptr, 0, &typeLen);
    previewId = new char[typeLen + 1];
    napi_get_value_string_utf8(env, args[3], previewId, typeLen + 1, &typeLen);

    char *photoId = nullptr;
    napi_get_value_string_utf8(env, args[4], nullptr, 0, &typeLen);
    photoId = new char[typeLen + 1];
    napi_get_value_string_utf8(env, args[4], photoId, typeLen + 1, &typeLen);

    // 获取surfaceId
    OHNativeWindow *window;
    int resultCode = OH_AVRecorder_GetInputSurface(g_avRecorder, &window);
    if (resultCode != AV_ERR_OK) {
        OH_LOG_INFO(LOG_APP, "---------NDK xts------prepare Camera error");
    }
    uint64_t surfaceId;
    OH_NativeWindow_GetSurfaceId(window, &surfaceId);
    char videoId[30];
    //     snprintf(LOG_APP,"?")
    OH_LOG_INFO(LOG_APP, "init Camera fouseMode : %{public}d", focusMode);
    OH_LOG_INFO(LOG_APP, "init Camera cameraDeviceIndex : %{public}d", cameraDeviceIndex);
    OH_LOG_INFO(LOG_APP, "init Camera sceneMode : %{public}d", sceneMode);
    OH_LOG_INFO(LOG_APP, "init Camera previewId : %{public}d", previewId);
    OH_LOG_INFO(LOG_APP, "init Camera photo : %{public}d", photoId);
    OH_LOG_INFO(LOG_APP, "init Camera video : %{public}d", videoId);
    
    ndkCamera_ = new NDKCamera(focusMode, cameraDeviceIndex, sceneMode, previewId, photoId, videoId);
    int result = 6;
    napi_value res;
    napi_create_int32(env, result, &res);
    return res;
}
// static napi_value setPreviewSurfaceId(napi_env env, napi_callback_info info) {}
// static napi_value setPreviewSurfaceId(napi_env env, napi_callback_info info) {}

EXTERN_C_START
static napi_value Init(napi_env env, napi_value exports) {
    napi_property_descriptor desc[] = {
        {"createAVRecorder", nullptr, createAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"prepareAVRecorder", nullptr, prepareAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"createPrepareAVRecorder", nullptr, createPrepareAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"startAVRecorder", nullptr, startAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"pauseAVRecorder", nullptr, pauseAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"resumeAVRecorder", nullptr, resumeAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"stopAVRecorder", nullptr, stopAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"resetAVRecorder", nullptr, resetAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"releaseAVRecorder", nullptr, releaseAVRecorder, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"updateRotationAVRecorder", nullptr, updateRotationAVRecorder, nullptr, nullptr, nullptr, napi_default,
         nullptr},
        {"prepareCamera", nullptr, prepareCamera, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"setPreviewSurfaceId", nullptr, setPreviewSurfaceId, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getAVRecorderConfig", nullptr, getAVRecorderConfig, nullptr, nullptr, nullptr, napi_default, nullptr},
        {"getAvailableEncoder", nullptr, getAvailableEncoder, nullptr, nullptr, nullptr, napi_default, nullptr},
    };
    napi_define_properties(env, exports, sizeof(desc) / sizeof(desc[0]), desc);
    return exports;
}
EXTERN_C_END

static napi_module demoModule = {
    .nm_version = 1,
    .nm_flags = 0,
    .nm_filename = nullptr,
    .nm_register_func = Init,
    .nm_modname = "avrecorderndk",
    .nm_priv = ((void *)0),
    .reserved = {0},
};

extern "C" __attribute__((constructor)) void RegisterEntryModule(void) { napi_module_register(&demoModule); }